﻿using Microsoft.Web.Administration;
using SocketCommon;
using SocketModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Windows.Threading;

namespace SocketService
{
    public class ServiceMainWindowViewModel : ObservableObject
    {
        public ServiceMainWindowViewModel()
        {
            Init();
        }
        public void Init()
        {
            //端口号（用来监听的）
            int port = Convert.ToInt32(ConfigurationManager.AppSettings["Port"]);

            string host = ConfigurationManager.AppSettings["ServiceIp"];
            IPAddress ip = IPAddress.Parse(host);

            //将IP地址和端口号绑定到网络节点point上  
            IPEndPoint ipe = new IPEndPoint(ip, port);

            //定义一个套接字用于监听客户端发来的消息，包含三个参数（IP4寻址协议，流式连接，Tcp协议）  
            SocketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //监听绑定的网络节点  
            SocketWatch.Bind(ipe);
            //将套接字的监听队列长度限制为20  
            SocketWatch.Listen(20);
            //负责监听客户端的线程:创建一个监听线程  
            Thread threadwatch = new Thread(WatchConnecting);
            //将窗体线程设置为与后台同步，随着主线程结束而结束  
            threadwatch.IsBackground = true;
            //启动线程     
            threadwatch.Start();
            LogText = "开启监听......";
            //SocketWatch.Close();
        }

        //创建一个和客户端通信的套接字
        static Socket SocketWatch = null;
        //定义一个集合，存储客户端信息
        static Dictionary<string, Socket> ClientConnectionItems = new Dictionary<string, Socket> { };

        //监听客户端发来的请求  
        public void WatchConnecting()
        {
            Socket connection = null;

            //持续不断监听客户端发来的请求     
            while (true)
            {
                try
                {
                    connection = SocketWatch.Accept();
                }
                catch (Exception ex)
                {
                    //提示套接字监听异常     
                    LogText = (ex.Message.ToString());
                    break;
                }

                //客户端网络结点号  
                string remoteEndPoint = connection.RemoteEndPoint.ToString();
                //添加客户端信息  
                ClientConnectionItems.Add(remoteEndPoint, connection);
                //显示与客户端连接情况
                ClientListModel.ContentText = remoteEndPoint.ToString();
                //("\r\n[客户端\"" + remoteEndPoint + "\"建立连接成功！ 客户端数量：" + ClientConnectionItems.Count + "]");
                ThreadPool.QueueUserWorkItem(delegate
                {
                    SynchronizationContext.SetSynchronizationContext(new
                    DispatcherSynchronizationContext(System.Windows.Application.Current.Dispatcher));
                    SynchronizationContext.Current.Post(pl =>
                    {
                        //解决异步线程调用回归主线程
                        //System.NotSupportedException:“该类型的 CollectionView 不支持从调度程序线程以外的线程对其 SourceCollection 进行的更改。”
                        ClientList.Add(ClientListModel);
                    }, null);
                });
                LogText = "客户端数量：" + ClientConnectionItems.Count.ToString();
                //获取客户端的IP和端口号  
                IPAddress clientIP = (connection.RemoteEndPoint as IPEndPoint).Address;
                int clientPort = (connection.RemoteEndPoint as IPEndPoint).Port;

                //让客户显示"连接成功的"的信息  
                //string sendmsg = "[" + "本地IP：" + clientIP + " 本地端口：" + clientPort.ToString() + " 连接服务端成功！]";
                string sendmsg = clientIP + ":" + clientPort.ToString();
                byte[] arrSendMsg = Encoding.UTF8.GetBytes(sendmsg);
                //connection.Send(arrSendMsg);
                //connection.Send
                //Thread.Sleep(15000);
                string Denglu = string.Empty;
                foreach (Socket item in ClientConnectionItems.Values)
                {
                    Denglu += item.RemoteEndPoint.ToString() + "@";
                }
                try
                {
                    foreach (var socketTemp in ClientConnectionItems)
                    {
                        socketTemp.Value.Send(Encoding.UTF8.GetBytes("Login@" + Denglu));
                    }
                }
                catch (Exception)
                {

                    throw;
                }
                //创建一个通信线程      
                Thread thread = new Thread(ReceiveMsg)
                {
                    //设置为后台线程，随着主线程退出而退出 
                    IsBackground = true//后台启动
                };
                //启动线程     
                thread.Start(connection);//有传入参数的线程
            }
        }

        /// <summary>
        /// 接收客户端发来的信息，客户端套接字对象
        /// </summary>
        /// <param name="socketclientpara"></param>    
        public void ReceiveMsg(object socketclientpara)
        {
            Socket socketServer = socketclientpara as Socket;
            long fileLength = 0;//文件长度
            string recStr = null;//文件名
            while (true)
            {
                //创建一个内存缓冲区，其大小为1024*1024字节  即1M     
                byte[] arrServerRecMsg = new byte[1024 * 1024 * 5];
                //将接收到的信息存入到内存缓冲区，并返回其字节数组的长度    
                try
                {
                    int length = 0;//接收到信息的字节长度
                    if (socketServer != null)
                    {
                        length = socketServer.Receive(arrServerRecMsg);
                    }

                    //将机器接受到的字节数组转换为人可以读懂的字符串     
                    string strSRecMsg = Encoding.UTF8.GetString(arrServerRecMsg, 0, length);

                    string startSiteName = "";  //需要变更的站点

                    //将发送的字符串信息附加到文本框txtMsg上     
                    var ss = ("\r\n[客户端：" + socketServer.RemoteEndPoint + " 时间：" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff") + "]\r\n" + strSRecMsg);
                    if (strSRecMsg.Contains("下线"))
                    {
                        ThreadPool.QueueUserWorkItem(delegate
                        {
                            SynchronizationContext.SetSynchronizationContext(new
                            DispatcherSynchronizationContext(System.Windows.Application.Current.Dispatcher));
                            SynchronizationContext.Current.Post(pl =>
                            {
                                //解决异步线程调用回归主线程
                                //System.NotSupportedException:“该类型的 CollectionView 不支持从调度程序线程以外的线程对其 SourceCollection 进行的更改。”
                                ClientList.RemoveAt(ClientList.ToList().FindIndex(t => t.ContentText == socketServer.RemoteEndPoint.ToString()));

                            }, null);
                        });
                    }
                    if (length > 0)//大于0，说明有东西传过来
                    {
                        if (arrServerRecMsg[0] == 0)//0对应文字信息
                        {
                            string recMsg = Encoding.UTF8.GetString(arrServerRecMsg, 1, length - 1);
                        }
                        if (arrServerRecMsg[0] == 1)//1对应文件信息
                        {
                            SaveClientSendFile(socketServer, fileLength, recStr, arrServerRecMsg, length);
                        }
                        if (arrServerRecMsg[0] == 2)//2对应文件名字和长度
                        {
                            string fileNameWithLength = Encoding.UTF8.GetString(arrServerRecMsg, 1, length - 1);
                            recStr = fileNameWithLength.Split('-').First();//获取文件名
                            fileLength = Convert.ToInt64(fileNameWithLength.Split('-').Last());//获取文件长度
                        }
                    }
                    //普通发送信息
                    SenfMsg(socketServer, strSRecMsg);
                    //IIS状态改变命令
                    if (strSRecMsg.Contains("重启") || strSRecMsg.Contains("启动") || strSRecMsg.Contains("停止"))
                    {
                        startSiteName = SetIISState(socketServer, strSRecMsg);
                    }
                    //发布站点命令
                    else if (strSRecMsg.Contains("发布"))
                    {
                        strSRecMsg = PublishSite(socketServer, strSRecMsg);
                    }
                }
                catch (Exception)
                {
                    ClientConnectionItems.Remove(socketServer.RemoteEndPoint.ToString());
                    //提示套接字监听异常  
                    Console.WriteLine("\r\n[客户端\"" + socketServer.RemoteEndPoint + "\"已经中断连接！ 客户端数量：" + ClientConnectionItems.Count + "]");
                    //关闭之前accept出来的和客户端进行通信的套接字 
                    socketServer.Close();
                    break;
                }
            }
        }
        /// <summary>
        /// 保存客户端上传的文件
        /// </summary>
        /// <param name="socketServer"></param>
        /// <param name="fileLength"></param>
        /// <param name="recStr"></param>
        /// <param name="arrServerRecMsg"></param>
        /// <param name="length"></param>
        private static void SaveClientSendFile(Socket socketServer, long fileLength, string recStr, byte[] arrServerRecMsg, int length)
        {
            SaveFileDialog sfDialog = new SaveFileDialog();//创建SaveFileDialog实例
            string spath = ConfigurationManager.AppSettings["Path"];//制定存储路径
            string savePath = Path.Combine(spath, recStr);//获取存储路径及文件名
            int rec = 0;
            long recFileLength = 0;
            bool firstWrite = true;
            using (FileStream fs = new FileStream(savePath, FileMode.Create, FileAccess.Write))
            {
                while (recFileLength < fileLength)
                {
                    if (firstWrite)
                    {
                        fs.Write(arrServerRecMsg, 1, length - 1);
                        fs.Flush();
                        recFileLength += length - 1;
                        firstWrite = false;
                    }
                    else
                    {
                        rec = socketServer.Receive(arrServerRecMsg);
                        fs.Write(arrServerRecMsg, 0, rec);
                        fs.Flush();
                        recFileLength += rec;
                    }
                }
                fs.Close();
            }
        }

        /// <summary>
        /// 解压新站点文件发布站点信息
        /// </summary>
        /// <param name="socketServer"></param>
        /// <param name="strSRecMsg"></param>
        /// <returns></returns>
        private string PublishSite(Socket socketServer, string strSRecMsg)
        {
            string[] strs = new string[2];
            string msg = "";
            //待解压的文件
            strs[0] = ConfigurationManager.AppSettings["CompressPath"]; //"D:\\i4Tools7\\Redis安装.zip";
            //解压后放置的目标文件
            strs[1] = ConfigurationManager.AppSettings["DecompressionPath"]; //"D:\\Debug3\\";
            UnZipFloClass uzc = new UnZipFloClass();
            //解压文件
            uzc.unZipFile(strs[0], strs[1], ref msg);
            if (msg == "解压成功")
            {
                strSRecMsg = strSRecMsg.Replace("发布", "重启");
                SetIISState(socketServer, strSRecMsg);
                SenfMsg(socketServer, "网站发布成功，站点重启完成");
            }
            return strSRecMsg;
        }

        /// <summary>
        /// 设置IIS状态
        /// </summary>
        /// <param name="socketServer"></param>
        /// <param name="strSRecMsg">Socket接收到的信息</param>
        /// <returns></returns>
        private string SetIISState(Socket socketServer, string strSRecMsg)
        {
            string startSiteName = strSRecMsg.Remove(0, 2);
            webManager = new Microsoft.Web.Administration.ServerManager();
            foreach (var site in webManager.Sites)
            {
                if (strSRecMsg.Contains("重启") && site.Name == startSiteName)
                {
                    site.Stop();
                    site.Start();
                    SenfMsg(socketServer, "重启成功");
                }
                if (strSRecMsg.Contains("启动") && site.Name == startSiteName)
                {
                    site.Start();
                    SenfMsg(socketServer, "启动成功");
                }
                if (strSRecMsg.Contains("停止") && site.Name == startSiteName)
                {
                    site.Stop();
                    SenfMsg(socketServer, "停止成功");
                }

            }
            return startSiteName;
        }
        /// <summary>
        /// 向客户端发送信息
        /// </summary>
        /// <param name="socketServer"></param>
        /// <param name="strmsg">发送内容</param>
        private static void SenfMsg(Socket socketServer, string strmsg)
        {
            if (ClientConnectionItems.Count > 0)
            {
                foreach (var socketTemp in ClientConnectionItems)
                {
                    socketTemp.Value.Send(Encoding.UTF8.GetBytes(("\r\n[客户端：" + socketServer.RemoteEndPoint + " 时间：" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff") + "]\r\n" + strmsg)));
                }
            }
        }

        private BsLinkRelated _clientListModel = new BsLinkRelated();
        private ObservableCollection<BsLinkRelated> _clientList = new ObservableCollection<BsLinkRelated>();
        private string _LogText = string.Empty;
        private ServerManager webManager;

        public BsLinkRelated ClientListModel
        {
            get { return _clientListModel; }
            set { this.SetProperty(ref _clientListModel, value); }
        }
        public ObservableCollection<BsLinkRelated> ClientList
        {
            get { return _clientList; }
            set { this.SetProperty(ref _clientList, value); }
        }
        public string LogText
        {
            get { return _LogText; }
            set { this.SetProperty(ref _LogText, value); }
        }
    }
}
